import difflib
import librosa
import re
import numpy as np
import nltk
from nltk.corpus import cmudict

# 确保cmudict数据已下载
try:
    nltk.data.find('corpora/cmudict')
except LookupError:
    nltk.download('cmudict')

cmu_dict = cmudict.dict()

def normalize_text(text):
    """规范化文本，移除标点并转为小写"""
    normalized = re.sub(r'[^\w\s]', '', text).lower().strip()
    return normalized

def word_to_phonemes(word):
    """将单词转换成音素序列，若不在词典中则返回空列表"""
    return cmu_dict[word][0] if word in cmu_dict else []

def text_to_phonemes(text):
    """将文本转换成音素序列"""
    words = normalize_text(text).split()
    phonemes = []
    for word in words:
        phonemes.extend(word_to_phonemes(word))
    return phonemes

def extract_features(audio_path):
    """提取音频特征（如音高、强度、语速等）并评估发音质量"""
    y, sr = librosa.load(audio_path, sr=None)

    # 提取音高（Pitch）
    pitch, voiced_flag, voiced_probs = librosa.pyin(y, fmin=librosa.note_to_hz('C2'), fmax=librosa.note_to_hz('C7'))

    # 音量强度
    intensity = np.abs(y).mean()

    # 语速（每分钟音节数）
    syllables = len(librosa.effects.split(y, top_db=20))  # 简单估算音节数量
    speech_rate = syllables / (len(y) / sr / 60)

    return pitch, intensity, speech_rate

def load_dictionary(dict_path):
    """加载词典"""
    word_phonetics_dict = {}
    with open(dict_path, "r", encoding="utf-8") as file:
        for line in file:
            word, phonetic = line.strip().split(":")
            word_phonetics_dict[word] = phonetic
    return word_phonetics_dict

def compare_texts(user_text, reference_text):
    """对比用户文本和参考文本并提供详细反馈"""
    user_words = normalize_text(user_text).split()
    reference_words = normalize_text(reference_text).split()

    seq_matcher = difflib.SequenceMatcher(None, user_words, reference_words)
    differences = [(tag, user_words[i1:i2], reference_words[j1:j2])
                   for tag, i1, i2, j1, j2 in seq_matcher.get_opcodes() if tag != 'equal']

    return differences

def generate_readable_feedback_with_phonetics(differences, user_words, reference_words, word_phonetics_dict):
    """基于单词差异和音标字典生成易于理解的反馈"""
    feedback = []
    for tag, user_section, ref_section in differences:
        if tag == 'replace':
            # 对于替换操作，我们逐个单词地给出反馈
            for uw, rw in zip(user_section, ref_section):
                if rw in word_phonetics_dict:  # 如果参考单词在我们的词典中
                    correct_pronunciation = word_phonetics_dict[rw]
                    feedback.append(f"您在读'{rw}'时发音不标准，请注意它的正确发音是 {correct_pronunciation}.")
        elif tag == 'delete':
            # 对于删除操作，指明缺少了哪些单词
            for word in ref_section:
                feedback.append(f"缺少了单词‘{word}’。")
        elif tag == 'insert':
            # 对于插入操作，指明额外添加了哪些不必要的单词
            for word in user_section:
                feedback.append(f"额外添加了不必要的单词‘{word}’。")
    return feedback

def evaluate_pronunciation(user_text, reference_text, audio_path):
    """评估用户的发音并给出反馈"""
    # 加载词典
    dict_path = "dictionary.txt"
    word_phonetics_dict = load_dictionary(dict_path)

    differences = compare_texts(user_text, reference_text)
    pitch, intensity, speech_rate = extract_features(audio_path)

    # 提取音素序列
    user_phonemes = text_to_phonemes(user_text)
    reference_phonemes = text_to_phonemes(reference_text)

    # 计算文本匹配得分
    seq_matcher = difflib.SequenceMatcher(None, user_text, reference_text)
    text_match_score = seq_matcher.ratio() * 100

    # 初始化音频得分
    audio_score = 100
    pitch_score = 100
    intensity_score = 100
    speech_rate_score = 100

    # 根据音高调整评分
    if pitch.mean() < 100 or pitch.mean() > 300:  # 音高过低或过高
        pitch_score -= 10
        audio_score -= 10
    # 根据音量强度调整评分
    if intensity < 0.01:  # 音量过低
        intensity_score -= 10
        audio_score -= 10
    # 根据语速调整评分
    if speech_rate < 100:  # 语速过慢
        speech_rate_score -= 10
        audio_score -= 10
    elif speech_rate > 200:  # 语速过快
        speech_rate_score -= 10
        audio_score -= 10

    # 综合评分
    phoneme_match_score = (len(reference_phonemes) - len(differences)) / len(reference_phonemes) * 100 if reference_phonemes else 0
    # 综合评分
    overall_score = 0.3 * phoneme_match_score + 0.3 * text_match_score + 0.4 * audio_score

    # 根据音素差异生成反馈
    user_words = normalize_text(user_text).split()
    reference_words = normalize_text(reference_text).split()
    feedback = generate_readable_feedback_with_phonetics(differences, user_words, reference_words, word_phonetics_dict)

    if pitch.mean() < 100:
        feedback.append("您的音调偏低，请适当提高音调。")
    elif pitch.mean() > 300:
        feedback.append("您的音调偏高，请适当降低音调。")
    if intensity < 0.01:
        feedback.append("您的声音较小，请尽量大声说话。")
    if speech_rate < 100:
        feedback.append("您的语速较慢，请尝试加快速度。")
    elif speech_rate > 200:
        feedback.append("您的语速较快，请尝试放慢速度。")

    return {
        'feedback': feedback,
        'score': round(overall_score, 2),  # 保留两位小数
        'text_match_score': round(text_match_score, 2),
        'pitch_score': round(pitch_score, 2),
        'intensity_score': round(intensity_score, 2),
        'speech_rate_score': round(speech_rate_score, 2)
    }